/*
 * @Author: wuzhouhai wzh200x@126.com
 * @Date: 2024-03-20 14:01:01
 * @LastEditors: wuzhouhai wzh200x@126.com
 * @LastEditTime: 2024-05-24 11:17:10
 * @FilePath: \phoenix_client_sdk\Phoenix.ts
 * @Description:
 */
import { LoginAccountInput } from './dto/login-account.input';
import { Account } from './entities/Account';
import { Avatar } from './entities/Avatar';
import { PhoenixHttpClient } from './httpClient/PhoenixHttpClient';
import { Utils } from './utils/Utils';
import { AccountResponse } from './response/account.response';
import { ErrorLocalCode } from './defines/error.local.define';
import { CreateAvatarInput } from './dto/create-avatar.input';
import { AvatarResponse } from './response/avatar.response';
import { GetAvatarInput } from './dto/get-avatar.input';
import { UtilsString } from './utils/Utils.String';
import { UpdateAvatarInput } from './dto/update-avatar.input';
import { AvatarUpdateResponse } from './response/avatar.update.response';
import { UpdateTokenInput } from './dto/update-token.input';
import { UpdateTokenResponse } from './response/update.token.response';
import { UpdateAccountInput } from './dto/update-account.input';
import { UpdateServerTimeInput } from './dto/get-serverTime.input';
import { PlatformDefine } from './defines/platform.define';
import { IPlatform } from './interface/platform.interface';
import { PlatformManager } from './platform/platformManager';
import PhoenixComponent from './PhoenixComponent';
import { AlipayReportInput } from './dto/alipay.report.input';
import { ServerInfoResponse } from './response/server.response';
import { InputBase } from './dto/input-base';
import { QueryAccountInput } from './dto/query-account.input';
import { CenterUpdateInput } from './dto/center-update.input';
import { CallThirdInput } from './dto/call-third.input';
import { LoginPlatformInput } from './dto/platform-login.input';
import { PlatformResponse } from './response/platform.response';

const SERVERS = ['manyoukeji2024.com', 'qianyudao.com'];
const RETRY_COUNT = SERVERS.length;

export class Phoenix {
    private platform: PlatformDefine;
    private platformHelp: IPlatform;
    private httpClient: PhoenixHttpClient;
    private gameServerHttpClient: PhoenixHttpClient;
    private account: Account;
    private avatar: Avatar<any>;
    private serverTimer: number;
    private phoenixComponent: PhoenixComponent;
    private serverInfo: ServerInfoResponse;
    private selectServerZone: number;
    private thirdCallBackData: any;
    private index: number = 0;
    private hosts: string[] = [];
    private port: string = '';
    private isSSL: boolean = false;
    constructor() {}

    public init(platform: PlatformDefine, host: string, port: string, isSSL: boolean, secretKey?: string) {
        this.selectServerZone = -1;
        this.platform = platform;
        this.platformHelp = PlatformManager.GeneratePlatform(platform);
        this.port = port;
        this.isSSL = isSSL;

        let [thirdDomain] = host.match(/^\w+\./);
        if (thirdDomain && thirdDomain.search('test') < 0) {
            SERVERS.forEach((domain) => {
                this.hosts.push(thirdDomain + domain);
            });
            this.httpClient = new PhoenixHttpClient(this.platformHelp, this.hosts[0], port, isSSL, secretKey);
        } else {
            this.httpClient = new PhoenixHttpClient(this.platformHelp, host, port, isSSL, secretKey);
        }
    }

    public setPhoenixComponent(phoenixComponent: PhoenixComponent) {
        this.phoenixComponent = phoenixComponent;
    }

    public initAccount(name: string, password: string) {
        this.account = new Account(name, password);
    }

    public isThirdPlatform(): boolean {
        return this.platform != PlatformDefine.PhoenixGame;
    }

    public checkLoginHasAvatar(): boolean {
        if (this.account == null) {
            return false;
        }
        if (this.account.getAvatarId() == 0) {
            return false;
        }
        return true;
    }

    private initAvatar<T>(avatarResponse: AvatarResponse) {
        this.avatar = new Avatar<T>(avatarResponse.name);
        this.avatar.setAvatarData(JSON.parse(avatarResponse.data) as T);
        this.avatar.setId(avatarResponse.id);
        this.avatar.setCreateTime(avatarResponse.createTime);
        this.avatar.setCredential(avatarResponse.credential);
        this.avatar.setIv(avatarResponse.iv);
    }

    public clear() {
        this.account = null;
        this.avatar = null;
    }

    public async thirdLogin(thirdCode: string = '', cb): Promise<number> {
        if (this.account == null) {
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        let url = '/auth/platformLogin';
        let input = new LoginPlatformInput();
        input.thirdCode = thirdCode;
        input.thirdPlatformType = this.platform;
        await this.requestPost<any>(
            url,
            input,
            (re) => {
                const platformRes: PlatformResponse = re.data;
                this.account.setAccountName(platformRes.thirdAccount);
                cb(0);
            },
            (e) => {
                cb(e.code);
            }
        );
        return 0;
    }

    public async login(thirdCode: string = ''): Promise<Number> {
        if (this.gameServerHttpClient == null) {
            return ErrorLocalCode.SERVER_NOT_SELECT;
        }
        console.log('login thirdCode:', thirdCode);
        if (
            this.account == null ||
            this.account.getAccountName() == '' ||
            this.account.getAccountName() == null ||
            this.account.getAccountName() == undefined
        ) {
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        let url = '/auth/login';
        let input = new LoginAccountInput();
        input.accountName = this.account.getAccountName();
        input.password = this.account.getAccountPassword();
        input.thirdCode = thirdCode;
        input.thirdPlatformType = this.platform;
        if (this.platform == PlatformDefine.PhoenixGame) {
            if (input.accountName == '' || input.password == '') {
                return -1;
            }
        }
        if (input.password != '') {
            input.password = Utils.Md5Encode(input.password);
        }
        let opCode = 0;
        await this.gameServerHttpClient
            .requestPost<any>(url, input)
            .then(async (re) => {
                if (re.code == 0) {
                    const reAccount: AccountResponse = re.data;
                    this.account.setAccountName(reAccount.accountName);
                    this.account.setId(reAccount.accountId);
                    this.account.setToken(reAccount.accessToken);
                    this.account.setAvatarId(reAccount.avatarId);
                    this.account.setThirdAccount(reAccount.thirdAccount);
                    this.account.setTokenExpireTime(reAccount.tokenExpireTime);
                }
                opCode = re.code ?? ErrorLocalCode.UNKNOWN_ERROR;
            })
            .catch((e) => {
                opCode = e.code;
            });
        return opCode;
    }

    public async createAvatar<T>(avatarData: T | string, name: string = ''): Promise<Number> {
        if (this.gameServerHttpClient == null) {
            return ErrorLocalCode.SERVER_NOT_SELECT;
        }
        if (this.account == null) {
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        let url = '/avatar/create';
        let input = new CreateAvatarInput();
        input.account_id = this.account.getId();
        input.name = name;
        input.data = typeof avatarData == 'string' ? avatarData : JSON.stringify(avatarData);
        input.data = UtilsString.zlibEncode(input.data);
        let opCode = 0;
        await this.gameServerHttpClient
            .requestPost<any>(url, input, this.account.getToken())
            .then((re) => {
                if (re.code == 0) {
                    const reAvatar: AvatarResponse = re.data;
                    reAvatar.data = UtilsString.zlibDecode(reAvatar.data);
                    this.initAvatar<T>(reAvatar);
                }
                opCode = re.code ?? ErrorLocalCode.UNKNOWN_ERROR;
            })
            .catch((e) => {
                opCode = e.code;
            });
        return opCode;
    }

    public async getAvatar<T>(): Promise<Number> {
        if (this.gameServerHttpClient == null) {
            return ErrorLocalCode.SERVER_NOT_SELECT;
        }
        if (this.account == null) {
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        let url = '/avatar/query';
        let input = new GetAvatarInput();
        input.account_id = this.account.getId();
        let opCode = 0;
        await this.gameServerHttpClient
            .requestPost<any>(url, input, this.account.getToken())
            .then((re) => {
                if (re.code == 0) {
                    const reAvatar: AvatarResponse = re.data;
                    reAvatar.data = UtilsString.zlibDecode(reAvatar.data);
                    this.initAvatar<T>(reAvatar);
                }
                opCode = re.code ?? ErrorLocalCode.UNKNOWN_ERROR;
            })
            .catch((e) => {
                opCode = e.code;
            });
        return opCode;
    }

    public async updateAvtar(): Promise<Number> {
        if (this.gameServerHttpClient == null) {
            return ErrorLocalCode.SERVER_NOT_SELECT;
        }
        if (this.account == null) {
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        if (this.avatar == null) {
            return ErrorLocalCode.AVATAR_NOT_CREATE;
        }
        let url = '/avatar/update';
        let input = new UpdateAvatarInput();
        input.account_id = this.account.getId();
        input.avatar_id = this.avatar.getId();
        input.data = UtilsString.zlibEncode(JSON.stringify(this.avatar.getAvatarData()));
        let opCode = 0;
        await this.gameServerHttpClient
            .requestPost<any>(url, input, this.account.getToken())
            .then((re) => {
                if (re.code == 0) {
                    const reUpdate: AvatarUpdateResponse = re.data;
                    this.avatar.setUpdateTime(reUpdate.updateTime);
                }
                opCode = re.code ?? ErrorLocalCode.UNKNOWN_ERROR;
            })
            .catch((e) => {
                opCode = e.code;
            });
        return opCode;
    }

    public async updateAccountToken(): Promise<Number> {
        if (this.gameServerHttpClient == null) {
            return ErrorLocalCode.SERVER_NOT_SELECT;
        }
        if (this.account == null) {
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        let url = '/auth/update/token';
        let input = new UpdateTokenInput();
        input.account_id = this.account.getId();
        let opCode = 0;
        await this.gameServerHttpClient
            .requestPost<any>(url, input, this.account.getToken())
            .then((re) => {
                if (re.code == 0) {
                    const reUpdate: UpdateTokenResponse = re.data;
                    this.account.setToken(reUpdate.accessToken);
                    this.account.setTokenExpireTime(reUpdate.tokenExpireTime);
                }
                opCode = re.code ?? ErrorLocalCode.UNKNOWN_ERROR;
            })
            .catch((e) => {
                opCode = e.code;
            });
        return opCode;
    }

    public async updateThirdAccountToken(): Promise<Number> {
        if (this.gameServerHttpClient == null) {
            return ErrorLocalCode.SERVER_NOT_SELECT;
        }
        if (this.account == null) {
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        let url = '/account/update/third';
        let input = new UpdateAccountInput();
        input.account_id = this.account.getId();
        input.thirdToken = this.account.getThirdToken();
        input.thirdTokenExpireTime = this.account.getThirdTokenExpireTime();
        let opCode = 0;
        await this.gameServerHttpClient
            .requestPost<any>(url, input, this.account.getToken())
            .then((re) => {})
            .catch((e) => {
                opCode = e.code;
            });
        return opCode;
    }

    public async updateServerTimer(): Promise<Number> {
        if (this.gameServerHttpClient == null) {
            return ErrorLocalCode.SERVER_NOT_SELECT;
        }
        let url = '/time/now';
        let input = new UpdateServerTimeInput();
        let opCode = 0;
        await this.gameServerHttpClient
            .requestGet<any>(url, input)
            .then((re) => {
                this.serverTimer = re.data;
            })
            .catch((e) => {
                opCode = e.code;
            });
        return opCode;
    }

    public queryServerInfo(callback: Function) {
        let url = '/server/query?ver=new';
        let input = new InputBase();
        let opCode = 0;
        this.requestGet<any>(
            url,
            input,
            (re) => {
                this.serverInfo = re.data;
                callback && callback(opCode);
            },
            (e) => {
                opCode = e.code;
                callback && callback(opCode);
            }
        );
    }

    public async alipayReportAction(actionCode: string, actionFinishChannel: string): Promise<Number> {
        if (this.gameServerHttpClient == null) {
            return ErrorLocalCode.SERVER_NOT_SELECT;
        }
        if (this.account == null) {
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        let url = '/alipay-report/report';
        let input = new AlipayReportInput();
        input.account_id = this.account.getId();
        input.actionCode = actionCode;
        input.actionFinishChannel = actionFinishChannel;
        let opCode = 0;
        await this.gameServerHttpClient
            .requestPost<any>(url, input, this.account.getToken())
            .then((re) => {
                console.log('alipay report re:', re);
            })
            .catch((e) => {
                opCode = e.code;
            });
        return opCode;
    }

    public async serverCallThirdPlatform(thirdUrl: string, data: object): Promise<Number> {
        if (this.gameServerHttpClient == null) {
            return ErrorLocalCode.SERVER_NOT_SELECT;
        }
        if (this.account == null) {
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        let url = '/account/call/third';
        let input = new CallThirdInput();
        input.account_id = this.account.getId();
        input.url = thirdUrl;
        input.data = JSON.stringify(data);
        let opCode = 0;
        await this.gameServerHttpClient
            .requestPost<any>(url, input, this.account.getToken())
            .then((re) => {
                this.thirdCallBackData = re.data;
                console.log('alipay report re:', re);
            })
            .catch((e) => {
                opCode = e.code;
            });
        return opCode;
    }

    public queryAccount(callback: Function) {
        if (this.account == null) {
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        let url = '/center/query';
        let input = new QueryAccountInput();
        input.accountName = this.account.getAccountName();
        let opCode = 0;
        this.requestPost<any>(
            url,
            input,
            (re) => {
                this.account.setServers(re.data.servers);
                if (this.selectServerZone <= 0) {
                    let sid = this.account.getDefauteServer();
                    if (sid > 0) {
                        opCode = this.selectServerBySid(sid);
                    } else {
                        opCode = this.randomSelectServer();
                    }
                }
                callback && callback(opCode);
            },
            (e) => {
                opCode = e.code;
                callback && callback(opCode);
            }
        );
    }

    public updateCenter(callback: Function) {
        if (this.account == null) {
            callback && callback(ErrorLocalCode.ACCOUNT_NOT_CREATE);
            return ErrorLocalCode.ACCOUNT_NOT_CREATE;
        }
        if (this.selectServerZone <= 0) {
            callback && callback(ErrorLocalCode.SERVER_NOT_SELECT);
            return ErrorLocalCode.SERVER_NOT_SELECT;
        }
        let url = '/center/update';
        let input = new CenterUpdateInput();
        input.accountName = this.account.getAccountName();
        input.sid = this.selectServerZone;
        // 这里是处理第三方平台,在此时还没有获取到账户,后续登录成功后会再次上传
        if (input.accountName == '') {
            return 0;
        }
        let opCode = 0;
        this.requestPost<any>(
            url,
            input,
            (re) => {
                callback && callback(opCode);
            },
            (e) => {
                opCode = e.code;
                callback && callback(opCode);
            }
        );
    }

    public selectServerBySid(sid: number): number {
        for (let i = 0; i < this.serverInfo.servers.length; i++) {
            // 判断sid是否等于服务器ID
            if (this.serverInfo.servers[i].zone == sid) {
                this.selectServer(i);
                return ErrorLocalCode.SUCCESS;
            }
        }
        return ErrorLocalCode.SERVER_NOT_FOUNT;
    }

    public randomSelectServer(): number {
        if (this.serverInfo == null) {
            return ErrorLocalCode.SERVER_IS_NULL;
        }
        let index = Math.floor(Math.random() * this.serverInfo.servers.length);
        this.selectServer(index);
        return ErrorLocalCode.SUCCESS;
    }

    public selectServer(index?: number): number {
        if (this.serverInfo == null) {
            return ErrorLocalCode.SERVER_IS_NULL;
        }
        if (index == null || index == undefined) {
            this.gameServerHttpClient = new PhoenixHttpClient(
                this.platformHelp,
                this.serverInfo.default.web_address,
                this.serverInfo.default.web_port + '',
                this.serverInfo.default.web_ssl == 1,
                this.serverInfo.default.web_secret
            );
            this.selectServerZone = this.serverInfo.default.zone;
        } else {
            if (index > this.serverInfo.servers.length - 1) {
                return ErrorLocalCode.SERVER_INDEX_OUT_OF_RANGE;
            }
            this.gameServerHttpClient = new PhoenixHttpClient(
                this.platformHelp,
                this.serverInfo.servers[index].web_address,
                this.serverInfo.servers[index].web_port + '',
                this.serverInfo.servers[index].web_ssl == 1,
                this.serverInfo.servers[index].web_secret
            );
            this.selectServerZone = this.serverInfo.servers[index].zone;
        }
        this.updateServerTimer();
        return 0;
    }

    public getSelectServerZone(): number {
        return this.selectServerZone;
    }

    public getAccountIns(): Account {
        return this.account;
    }

    public getAvatarIns(): Avatar<any> {
        return this.avatar;
    }

    public getServerTimer(): number {
        return this.serverTimer;
    }

    public setServerTimer(serverTimer: number): void {
        this.serverTimer = serverTimer;
    }

    public thirdPlatformLogin(cb) {
        this.platformHelp.login(async (re) => {
            if (re == '') {
                cb(ErrorLocalCode.ACCOUNT_NOT_CREATE);
            } else {
                this.thirdLogin(re, cb);
            }
        });
    }

    public gameLogin(): Promise<void> {
        if (this.selectServerZone <= 0) {
            this.phoenixComponent.loginCallBack(false, ErrorLocalCode.SERVER_NOT_SELECT);
            return;
        }
        this.login().then((re) => {
            this.phoenixComponent.loginCallBack(re == 0, re.valueOf());
        });
    }

    public getThirdCallBackData(): any {
        return this.thirdCallBackData;
    }

    public async requestPost<T>(url: string, input: InputBase, success: Function, fail: Function, retryCount: number = RETRY_COUNT) {
        if (retryCount <= 0) {
            fail({ code: ErrorLocalCode.RETRY_SWITCH_DOMAIN_LIMIT, message: '域名切换已达上限' });
            return;
        }
        let httpClient = this.httpClient;
        httpClient
            .requestPost(url, input)
            .then((res) => {
                success(res);
            })
            .catch((err) => {
                if (err && err.code && err.code === ErrorLocalCode.REQUEST_FAILED) {
                    ++this.index;
                    if (this.index >= SERVERS.length) {
                        this.index = 0;
                    }
                    httpClient.setBaseUrl(this.hosts[this.index], this.port, this.isSSL);
                    this.requestPost(url, input, success, fail, --retryCount);
                }
            });
    }

    public async requestGet<T>(url: string, input: InputBase, success: Function, fail: Function, retryCount: number = RETRY_COUNT) {
        if (retryCount <= 0) {
            fail({ code: ErrorLocalCode.RETRY_SWITCH_DOMAIN_LIMIT, message: '域名切换已达上限' });
            return;
        }
        let httpClient = this.httpClient;
        httpClient
            .requestGet(url, input)
            .then((res) => {
                success(res);
            })
            .catch((err) => {
                if (err && err.code && err.code === ErrorLocalCode.REQUEST_FAILED) {
                    ++this.index;
                    if (this.index >= SERVERS.length) {
                        this.index = 0;
                    }
                    httpClient.setBaseUrl(this.hosts[this.index], this.port, this.isSSL);
                    this.requestGet(url, input, success, fail, --retryCount);
                }
            });
    }
}

export default new Phoenix();
